/// Author: QP
/// Version: 1.0
/// Date:2022/08
/// FHIR资源校正工具
/// notice: this class works on FHIR repository only!
Class HCC.Utils.FHIRTransformer Extends %RegisteredObject
{

/// find out existing encounter url from fhir repository
ClassMethod GetEncounterUrl(pEncounterNo, pServiceName = "/csp/healthshare/fhirns/fhir/r4") As %String
{
	Set tRet = ""
	Quit:(pEncounterNo="") ""
	
	Set tFHIRService = ##class(HS.FHIRServer.Service).EnsureInstance(pServiceName)
	
	Set tRequest = ##class(HS.FHIRServer.API.Data.Request).%New()
	Set tResponse = ##class(HS.FHIRServer.API.Data.Response).%New()
	
	Set tRequest.QueryString="identifier="_pEncounterNo
	Set tRequest.RequestMethod="GET"
	Set tRequest.RequestPath="/Encounter"
	Set tRequest.RequestFormatCode = "JSON"
	Set tRequest.ResponseFormatCode = tRequest.RequestFormatCode
	Do tFHIRService.DispatchRequest(tRequest, .tResponse)
	if ($ISOBJECT(tResponse.Json.entry) && (tResponse.Json.entry.%Size()>0))
	{
		Q $E(tResponse.Json.entry.%Get(0).fullUrl,2,)
	}
	
	Q tRet
}

/// find out existing patient url from fhir repository
ClassMethod GetPatientUrl(pNIDNo, pServiceName = "/csp/healthshare/fhirns/fhir/r4") As %String
{
	Set tRet = ""
	Quit:(pNIDNo="") ""
	
	Set tFHIRService = ##class(HS.FHIRServer.Service).EnsureInstance(pServiceName)
	
	Set tRequest = ##class(HS.FHIRServer.API.Data.Request).%New()
	Set tResponse = ##class(HS.FHIRServer.API.Data.Response).%New()
	
	Set tRequest.QueryString="identifier="_pNIDNo
	Set tRequest.RequestMethod="GET"
	Set tRequest.RequestPath="/Patient"
	Set tRequest.RequestFormatCode = "JSON"
	Set tRequest.ResponseFormatCode = tRequest.RequestFormatCode
	Do tFHIRService.DispatchRequest(tRequest, .tResponse)
	if ($ISOBJECT(tResponse.Json.entry) && (tResponse.Json.entry.%Size()>0))
	{
		Q $E(tResponse.Json.entry.%Get(0).fullUrl,2,)
	}
	
	Q tRet
}

/// find out existing location url from fhir repository
ClassMethod GetLocationUrl(pLocId, pServiceName = "/csp/healthshare/fhirns/fhir/r4") As %String
{
	Set tRet = ""
	Quit:(pLocId="") ""
	
	Set tFHIRService = ##class(HS.FHIRServer.Service).EnsureInstance(pServiceName)
	
	Set tRequest = ##class(HS.FHIRServer.API.Data.Request).%New()
	Set tResponse = ##class(HS.FHIRServer.API.Data.Response).%New()
	
	Set tRequest.QueryString="identifier="_pLocId
	Set tRequest.RequestMethod="GET"
	Set tRequest.RequestPath="/Location"
	Set tRequest.RequestFormatCode = "JSON"
	Set tRequest.ResponseFormatCode = tRequest.RequestFormatCode
	Do tFHIRService.DispatchRequest(tRequest, .tResponse)
	if ($ISOBJECT(tResponse.Json.entry) && (tResponse.Json.entry.%Size()>0))
	{
		Q $E(tResponse.Json.entry.%Get(0).fullUrl,2,)
	}
	
	Q tRet
}

/// find out existing Organization url from fhir repository
ClassMethod GetOrganizationUrl(pOrgId, pServiceName = "/csp/healthshare/fhirns/fhir/r4") As %String
{
	Set tRet = ""
	Quit:(pOrgId="") ""
	
	Set tFHIRService = ##class(HS.FHIRServer.Service).EnsureInstance(pServiceName)
	
	Set tRequest = ##class(HS.FHIRServer.API.Data.Request).%New()
	Set tResponse = ##class(HS.FHIRServer.API.Data.Response).%New()
	
	Set tRequest.QueryString="identifier="_pOrgId
	Set tRequest.RequestMethod="GET"
	Set tRequest.RequestPath="/Organization"
	Set tRequest.RequestFormatCode = "JSON"
	Set tRequest.ResponseFormatCode = tRequest.RequestFormatCode
	Do tFHIRService.DispatchRequest(tRequest, .tResponse)
	if ($ISOBJECT(tResponse.Json.entry) && (tResponse.Json.entry.%Size()>0))
	{
		Q $E(tResponse.Json.entry.%Get(0).fullUrl,2,)
	}
	
	Q tRet
}

/// find out existing Practitioner url from fhir repository
ClassMethod GetPractitionerUrl(pPracId, pServiceName = "/csp/healthshare/fhirns/fhir/r4") As %String
{
	Set tRet = ""
	Quit:(pPracId="") ""
	
	Set tFHIRService = ##class(HS.FHIRServer.Service).EnsureInstance(pServiceName)
	
	Set tRequest = ##class(HS.FHIRServer.API.Data.Request).%New()
	Set tResponse = ##class(HS.FHIRServer.API.Data.Response).%New()
	
	Set tRequest.QueryString="identifier="_pPracId
	Set tRequest.RequestMethod="GET"
	Set tRequest.RequestPath="/Practitioner"
	Set tRequest.RequestFormatCode = "JSON"
	Set tRequest.ResponseFormatCode = tRequest.RequestFormatCode
	Do tFHIRService.DispatchRequest(tRequest, .tResponse)
	if ($ISOBJECT(tResponse.Json.entry) && (tResponse.Json.entry.%Size()>0))
	{
		Q $E(tResponse.Json.entry.%Get(0).fullUrl,2,)
	}
	
	Q tRet
}

/// revise FHIR stream to replace patient and encounter reference with existing patient and encoutner url
ClassMethod ReviseFHIRStream(pSourceStream, ByRef pTargetStream) As %Status
{
	k ^||Loc,^||Pra,^||Org

	Set tSC	= $$$OK, (tPatientUrl, tEncounterUrl,tOrgUrl,tLocUrl,tPraUrl)=""

	Set tLen = 32000
	Set tFHIRSource = pSourceStream.Read(.tLen)
	While tLen>0
	{
		Set tFHIRSource = tFHIRSource_pSourceStream.Read(.tLen)
	}
	
	Set tSourceJSON = {}.%FromJSON(tFHIRSource)
		
	// quit if there is no data
	If ('$ISOBJECT(tSourceJSON.entry)) || (tSourceJSON.entry.%Size()=0) 
	{
		Set tSC = $$$ERROR(-10001,"Incorrect FHIR data in JSON format")
		Quit tSC
	}
	Set tSize = tSourceJSON.entry.%Size()
	
	Set:('$ISOBJECT(pTargetStream)) pTargetStream=##class(%Stream.TmpBinary).%New()
	Set tTargetJSON = {}.%New()
	Set tTargetJSON.resourceType = "Bundle"
    Set tTargetJSON.type = "transaction"
    Set tTargetJSON.entry=[]
	
	For i=1:1:tSize
	{
		Set tJObj = tSourceJSON.entry.%Get(i-1)
		// remove put operation, to avoid duplicated fullurl error
		/*If tJObj.request.method="PUT"
		{
			continue
		}*/
		// find out patint url first, if it exists already
		If tJObj.request.url="Patient"
		{
			continue:('$ISOBJECT(tJObj.resource.identifier))
			For j=1:1:tJObj.resource.identifier.%Size()
			{
				If tJObj.resource.identifier.%Get(j-1).type.text="SSN"
				{
					Set tPatientUrl = ..GetPatientUrl(tJObj.resource.identifier.%Get(j-1).value)
				}
				Else
				{
					if tJObj.resource.identifier.%Get(j-1).assigner.reference'=""
					{
						Set tRefLink = $G(^||Org(tJObj.resource.identifier.%Get(j-1).assigner.reference))
						Set:(tRefLink'="") tJObj.resource.identifier.%Get(j-1).assigner.reference = tRefLink
					}
				}
			}
			// if patient does not exist, push it into ist
			If tPatientUrl=""
			{
				If tJObj.resource.managingOrganization.reference '=""
				{
					Set tRefLink = $G(^||Org(tJObj.resource.managingOrganization.reference))
					Set:(tRefLink'="") tJObj.resource.managingOrganization.reference = tRefLink
				}
				Do tTargetJSON.entry.%Push(tJObj)
			}
		}
		// find out encounter url, if it exists already
		ElseIf tJObj.request.url="Encounter"
		{
			continue:('$ISOBJECT(tJObj.resource.identifier))
			
			For j=1:1:tJObj.resource.identifier.%Size()
			{
				If tJObj.resource.identifier.%Get(j-1).type.text="EncounterNumber"
				{
					Set tEncounterUrl = ..GetEncounterUrl(tJObj.resource.identifier.%Get(j-1).value)
					// quit this loop
					Quit
				}
			}
			// if encounter exists already, no process
			If tEncounterUrl=""
			{
				Set:((tJObj.resource.subject.reference '="")&&(tPatientUrl'="")) tJObj.resource.subject.reference = tPatientUrl
				// replace serviceProvider with existing Organization URL
				If (tJObj.resource.serviceProvider.reference '="")
				{
					Set tRefLink = $G(^||Org(tJObj.resource.serviceProvider.reference))
					Set:(tRefLink'="") tJObj.resource.serviceProvider.reference = tRefLink
				} 
				// replace location with existing Location URL (QP revised on 2022/8/15)
				If $ISOBJECT(tJObj.resource.location)
				{
					For j=1:1:tJObj.resource.location.%Size()
					{
						Set tLocRef = tJObj.resource.location.%Get(j-1).location.reference
						Set tRefLink = $G(^||Loc(tLocRef))
						Set:(tRefLink'="") tJObj.resource.location.%Get(j-1).location.reference = tRefLink
					} 
				}
				Do tTargetJSON.entry.%Push(tJObj)
			}
		}
		// find out organization url, if it exists already
		ElseIf tJObj.request.url="Organization"
		{
			Set tFullURL = tJObj.fullUrl,tOrgUrl=""
			continue:('$ISOBJECT(tJObj.resource.identifier)||(tFullURL=""))
			For j=1:1:tJObj.resource.identifier.%Size()
			{
				Set tOrgUrl = ..GetOrganizationUrl(tJObj.resource.identifier.%Get(j-1).value)
				// quit this loop
				If (tOrgUrl'="")
				{
					Set ^||Org(tFullURL)=tOrgUrl
					Quit
				}
			}
			// if organization does not exist, push it into list
			Do:(tOrgUrl="") tTargetJSON.entry.%Push(tJObj)
		}
		// find out Location url, if it exists already
		ElseIf tJObj.request.url="Location"
		{
			Set tFullURL = tJObj.fullUrl,tLocUrl=""
			continue:('$ISOBJECT(tJObj.resource.identifier)||(tFullURL=""))
			For j=1:1:tJObj.resource.identifier.%Size()
			{
				Set tLocUrl = ..GetLocationUrl(tJObj.resource.identifier.%Get(j-1).value)
				// quit this loop
				If (tLocUrl'="")
				{
					Set ^||Loc(tFullURL)=tLocUrl
					Quit
				}
			}
			// if Location exists already, no process
			Do:(tLocUrl="") tTargetJSON.entry.%Push(tJObj)
		}
		// find out Practitioner url, if it exists already
		ElseIf tJObj.request.url="Practitioner"
		{
			Set tFullURL = tJObj.fullUrl,tPraUrl=""
			continue:('$ISOBJECT(tJObj.resource.identifier) || (tFullURL=""))
			For j=1:1:tJObj.resource.identifier.%Size()
			{
				Set tPraUrl = ..GetPractitionerUrl(tJObj.resource.identifier.%Get(j-1).value)
				// quit this loop
				If (tPraUrl'="")
				{
					Set ^||Pra(tFullURL)=tPraUrl
					Quit
				}
			}
			// if Location exists already, no process
			Do:(tPraUrl="") tTargetJSON.entry.%Push(tJObj)
		}
		// for others, just modify their subject and encounter reference
		Else
		{
			Set:((tJObj.resource.encounter.reference '="")&&(tEncounterUrl'="")) tJObj.resource.encounter.reference = tEncounterUrl
			Set:((tJObj.resource.context.reference '="")&&(tEncounterUrl'="")) tJObj.resource.context.reference = tEncounterUrl
			
			Set:((tJObj.resource.subject.reference '="")&&(tPatientUrl'="")) tJObj.resource.subject.reference = tPatientUrl
			Set:((tJObj.resource.patient.reference '="")&&(tPatientUrl'="")) tJObj.resource.patient.reference = tPatientUrl
			
			if tJObj.resource.performer.actor.reference '=""
			{
				Set tRefLink = $G(^||Pra(tJObj.resource.performer.actor.reference))
				Set:(tRefLink'="") tJObj.resource.performer.actor.reference = tRefLink
			}
			Do tTargetJSON.entry.%Push(tJObj)
		}
	}
		
	// write data and save target file
	Do pTargetStream.Write(tTargetJSON.%ToJSON())
	
	// clean up and quit
	Do tJObj.%Close(),tSourceJSON.%Close(),tTargetJSON.%Close()
	
	k ^||Loc,^||Pra,^||Org
	Quit tSC
}

}
